/**
 * Class Jsl.Hash - hash collection implementation
 * 
 * @author  Mykhailo Stadnyk <mikhus@gmail.com>
 * @package Jsl
 * @version 1.0
 */
Jsl.Hash = Jsl.extend( Object, {

	/**
	 * @access private
	 */
	_hash  : null,
	_count : null,

	/**
	 * Class constructor - initializing the Hash
	 * 
	 * @param  {Object|Array} obj - hash data object
	 */
	constructor: function( obj) {
		this._hash = {};
		this._count = 0;
		if (obj instanceof Jsl.Hash) {
			this._hash  = obj.getData();
			this._count = obj.getCount();
		} else if (Jsl.isArray( obj)) {
			for (var i = 0, s = obj.length; i < s; i++)
				this.set( i, obj[i])
			;
		} else if (Jsl.isObject( obj)) {
			for (var i in obj)
				(!{}.hasOwnProperty || obj.hasOwnProperty( i)) && this.set( i, obj[i])
			;
		}
		Jsl.Hash.$super.constructor.call( this);
	},

	/**
	 * Sets a given value to Hash with a given key
	 * 
	 * @access public
	 * @param  {String}   key
	 * @param  {Mixed}    value
	 * @return {Jsl.Hash} this
	 */
	set : function( key, value) {
		if (!this.isset( key)) this._count++;
		this._hash[key] = value;
		return this;
	},

	/**
	 * Returns a value from hash by its key
	 * 
	 * @access public
	 * @param  {String} key
	 * @return {Mixed}  value
	 */
	get : function( key) { return this._hash[key]; },

	/**
	 * Checks if elements with given key exists in hash
	 * 
	 * @access public
	 * @param  {String}  key
	 * @return {Boolean} check result
	 */
	isset : function( key) { return Jsl.isDefined( this._hash[key]); },

	/**
	 * Unsets an element with given key from a hash
	 * 
	 * @access public
	 * @param  {String}   key
	 * @return {Jsl.Hash} this
	 */
	unset : function( key) {
		if (this.isset( key)) {
			this._hash[key] = undefined;
			delete this._hash[key];
			this._count--;
		}
		return this;
	},

	/**
	 * Returns a number of elements in hash
	 * 
	 * @access public
	 * @return {Integer} number of hash elements
	 */
	getCount : function() { return this._count; },

	/**
	 * Returns hash data object
	 * 
	 * @return {Object}
	 */
	getData : function() { return this._hash; },

	/**
	 * Iterating a hash, call an each iteration a given user-defined function
	 * There will be 3 arguments passed to user-defined function:
	 *  - {Mixed}   value    - hash element value
	 *  - {String}  key      - hash element key
	 *  - {Integer} iterator - numeric iterator
	 *  
	 *  @access public
	 *  @param  {Function}      - user defined function
	 *  @return {Jsl.Hash} this - self-object Jsl.Hash
	 */
	each : function( fn, scope) {
		var scope = scope || this;
		Jsl.each( this._hash, fn, scope);
		return this;
	},

	/**
	 * Merges one or more hash with current hash
	 * 
	 * @param  {Jsl.Hash|Object|Array} [hash1, hash2, ...]
	 * @return {Jsl.Hash} this - self Hash object
	 */
	merge : function() {
		for (var i = 0, s = arguments.length; i < s; i++) {
			var hash = arguments[i];
			if (hash instanceof Jsl.Hash)
				hash = hash.getData()
			;
			if (Jsl.isArray( hash) || Jsl.isObject( hash))
				Jsl.each( hash, function( v, k) {
					this.set( k, v);
				}, this)
			;
		}
		return this;
	}
});
